#include "mutmodel.h"
#include "mutresmanager.h"
CMUTTexManager g_texmanager;

struct MD5VERTEX{
	D3DXVECTOR4 pos;
	D3DXVECTOR3 normal;
	D3DXVECTOR3 tangent;
	D3DXVECTOR2 uv;
};

HRESULT CMUTParser::OpenHandle( HANDLE hFile ){
	fsize = GetFileSize( hFile, 0 );
	fileTmp = new char[fsize+1];
	DWORD read = 0;
	ReadFile( hFile, fileTmp, fsize, &read, 0 );
	if ( read != fsize ){
		MUTErrorTrace( L"read != fsize" );
		return E_FAIL;
	}
	fileTmp[ fsize ] = 0;
	curTmp = fileTmp;
	return S_OK;
}

void CMUTParser::Close(){
	if ( fileTmp )
		delete[] fileTmp;
	fileTmp = 0;
	fsize = 0;
}

////////////////////////////////////////////////////////////////////

bool CheckTGASuffix( const char* tmp ){
	int len = strlen( tmp );
	if ( len > 4 && stricmp( tmp + len - 4, ".tga" ) == 0 )
		return true;
	return false;
}
bool CheckTGASuffix( const WCHAR* tmp ){
	const WCHAR* dot = wcschr( tmp, L'.' );
	if ( dot && wcsnicmp( dot, L".tga", 4 ) == 0 )
		return true;
	return false;
}
void SkipUnnecessary( CMUTParser& parser );

CMUTMD5Shader* CMUTMD5Material::GetShader( TString& shader ){
	auto itr = mtr.find( shader );
	if ( itr != mtr.end() )
		return itr->second;
	return NULL;
}

void Heightmap2Normalmap( LPDIRECT3DTEXTURE9 pHeight, float strength, LPDIRECT3DTEXTURE9 pNormal ){
	D3DLOCKED_RECT normal_locked, height_locked;
	D3DSURFACE_DESC normal_desc;
	pNormal->LockRect( 0, &normal_locked, 0, 0 );
	pHeight->LockRect( 0, &height_locked, 0, 0 );
	pNormal->GetLevelDesc( 0, &normal_desc );
	
	for ( int y = 0; y < normal_desc.Height; y++ ){
		for ( int x = 0; x < normal_desc.Width; x++ ){
			LPDWORD ndwcolor = LPDWORD( LPBYTE( normal_locked.pBits ) + normal_locked.Pitch * y ) + x;
			LPDWORD h0dwcolor = LPDWORD( LPBYTE( height_locked.pBits ) + height_locked.Pitch * y ) + x;
			LPDWORD h1dwcolor, h2dwcolor, h3dwcolor, h4dwcolor;
			if ( y )
				h1dwcolor = LPDWORD( LPBYTE( height_locked.pBits ) + height_locked.Pitch * ( y - 1 ) ) + x;
			else
				h1dwcolor = h0dwcolor;
			if ( y < normal_desc.Height - 1 )
				h2dwcolor = LPDWORD( LPBYTE( height_locked.pBits ) + height_locked.Pitch * ( y + 1 ) ) + x;
			else
				h2dwcolor = h0dwcolor;
			if ( x )
				h3dwcolor = LPDWORD( LPBYTE( height_locked.pBits ) + height_locked.Pitch * y ) + x - 1;
			else
				h3dwcolor = h0dwcolor;
			if ( x < normal_desc.Width - 1 )
				h4dwcolor = LPDWORD( LPBYTE( height_locked.pBits ) + height_locked.Pitch * y ) + x + 1;
			else
				h4dwcolor = h0dwcolor;
			D3DXCOLOR h0 = *h0dwcolor, h1 = *h1dwcolor, h2 = *h2dwcolor, h3 = *h3dwcolor, h4 = *h4dwcolor;
			D3DXVECTOR3 n( strength* ( h0.r - h4.r ), strength * ( h0.r - h2.r ), 1.0f );
			D3DXVec3Normalize( &n, &n );
			D3DXCOLOR ncolor( (n.x+1.0f)*0.5f, (n.y+1.0f)*0.5f, (n.z+1.0f)*0.5f, 1.0f );
			*ndwcolor = ncolor;
		}
	}

	pHeight->UnlockRect( 0 );
	pNormal->UnlockRect( 0 );
}

void BumpAddnormals( LPDIRECT3DTEXTURE9 pNormal, LPDIRECT3DTEXTURE9 pHeight, float strength ){
	D3DLOCKED_RECT normal_locked, height_locked;
	D3DSURFACE_DESC normal_desc;
	pNormal->LockRect( 0, &normal_locked, 0, 0 );
	pHeight->LockRect( 0, &height_locked, 0, 0 );
	pNormal->GetLevelDesc( 0, &normal_desc );
	
	for ( int y = 0; y < normal_desc.Height; y++ ){
		for ( int x = 0; x < normal_desc.Width; x++ ){
			LPDWORD ndwcolor = LPDWORD( LPBYTE( normal_locked.pBits ) + normal_locked.Pitch * y ) + x;
			LPDWORD h0dwcolor = LPDWORD( LPBYTE( height_locked.pBits ) + height_locked.Pitch * y ) + x;
			LPDWORD h1dwcolor, h2dwcolor, h3dwcolor, h4dwcolor;
			if ( y )
				h1dwcolor = LPDWORD( LPBYTE( height_locked.pBits ) + height_locked.Pitch * ( y - 1 ) ) + x;
			else
				h1dwcolor = LPDWORD( LPBYTE( height_locked.pBits ) + height_locked.Pitch * (normal_desc.Height - 1) ) + x;
			if ( y < normal_desc.Height - 1 )
				h2dwcolor = LPDWORD( LPBYTE( height_locked.pBits ) + height_locked.Pitch * ( y + 1 ) ) + x;
			else
				h2dwcolor = LPDWORD( LPBYTE( height_locked.pBits ) ) + x;
			if ( x )
				h3dwcolor = LPDWORD( LPBYTE( height_locked.pBits ) + height_locked.Pitch * y ) + x - 1;
			else
				h3dwcolor = LPDWORD( LPBYTE( height_locked.pBits ) + height_locked.Pitch * y ) + normal_desc.Width - 1;
			if ( x < normal_desc.Width - 1 )
				h4dwcolor = LPDWORD( LPBYTE( height_locked.pBits ) + height_locked.Pitch * y ) + x + 1;
			else
				h4dwcolor = LPDWORD( LPBYTE( height_locked.pBits ) + height_locked.Pitch * y ) + 0;
			/**
				 h1
			  h3 h0 h4  n = ( h0-h4, h0-h2, 1 )
			     h2
			*/
			D3DXCOLOR ncolor = *ndwcolor;
			D3DXCOLOR h0 = *h0dwcolor, h1 = *h1dwcolor, h2 = *h2dwcolor, h3 = *h3dwcolor, h4 = *h4dwcolor;
			D3DXVECTOR3 n( 2.0f * ncolor.r - 1.0f, 2.0f * ncolor.b - 1.0f, 2.0f * ncolor.b - 1.0f );
			D3DXVec3Normalize( &n, &n );
			D3DXVECTOR3 n2( strength* ( h3.r - h4.r ), strength * ( h1.r - h2.r ), 0.2f );//( 2.0f*strength*(h3.r-h4.r), 2.0f*strength*(h1.r-h2.r), 4.0f );//
			D3DXVec3Normalize( &n2, &n2 );
			n += n2;
			D3DXVec3Normalize( &n, &n );
			ncolor = D3DXCOLOR( (n.x+1.0f)*0.5f, (n.y+1.0f)*0.5f, (n.z+1.0f)*0.5f, 1.0f );
			*ndwcolor = ncolor;
		}
	}

	pHeight->UnlockRect( 0 );
	pNormal->UnlockRect( 0 );
}

void ParseAddnormals( const WCHAR* mapname, TString& arg1, TString& arg2 ){
	static WCHAR tmp[256];
	wcscpy( tmp, mapname );

	WCHAR* first = wcschr( tmp, L'(' ) + 1;
	while ( !isalpha( *first ) && !isdigit( *first ) ) first++;

	WCHAR* second = wcschr( first, L',' );
	*second = 0;
	arg1 = first;

	while ( !isalpha( *second ) && !isdigit( *second ) ) second++;
	WCHAR* endbracket = second;
	int bracketcnt = 1;
	while ( bracketcnt ){
		endbracket++;
		if ( *endbracket == '(' )
			bracketcnt++;
		else if ( *endbracket == ')' )
			bracketcnt--;
	}
	*endbracket = 0;
	arg2 = second;
}

void ParseHeightmap( const WCHAR* mapname, TString& map, float& strength ){
	static WCHAR tmp[256];
	wcscpy( tmp, mapname );

	WCHAR* first = wcschr( tmp, L'(' ) + 1;
	while ( !isalpha( *first ) && !isdigit( *first ) ) first++;

	WCHAR* digit = wcschr( first, L',' );
	*digit = 0;
	map = first;
	if ( !CheckTGASuffix( map ) )
		map += TString( L".tga" );

	while ( !isdigit( *digit ) && *digit != L'.' ) digit++;
	WCHAR* rightbracket = wcschr( digit, L')' );
	*rightbracket = 0;
	strength = _wtof( digit );
}

HRESULT LoadBumpMap( const WCHAR* tmp, LPDIRECT3DTEXTURE9& pTex ){
	HRESULT hr = S_OK;
	if ( wcsncmp( tmp, L"addnormals", 10 ) == 0 ){
		TString arg1, arg2;
		ParseAddnormals( tmp, arg1, arg2 );
		if ( !CheckTGASuffix( arg1 ) )
			arg1 += TString( L".tga" );
		TString texname2;
		float strength;
		ParseHeightmap( arg2, texname2, strength );
		if ( FAILED( hr = D3DXCreateTextureFromFile( MUTState()->GetDevice(), arg1, &pTex ) ) ){
			MUTDXErrorTrace( hr, L"addnormals - normal map failed" );
			return E_FAIL;
		}
		LPDIRECT3DTEXTURE9 pHeightMap;
		if ( FAILED( hr = D3DXCreateTextureFromFile( MUTState()->GetDevice(), texname2, &pHeightMap ) ) ){
			MUTDXErrorTrace( hr, L"addnormals - heightmap failed" );
			return E_FAIL;
		}
		BumpAddnormals( pTex, pHeightMap, strength );
		pHeightMap->Release();
	}else if ( wcsncmp( tmp, L"heightmap", 9 ) == 0 ){
		TString texname;
		float strength = 0;
		ParseHeightmap( tmp, texname, strength );
		LPDIRECT3DTEXTURE9 pHeight = 0;
		if ( FAILED( hr = D3DXCreateTextureFromFile( MUTState()->GetDevice(), texname, &pHeight ) ) ){
			MUTDXErrorTrace( hr, L"height map failed" );
			return E_FAIL;
		}
		D3DSURFACE_DESC desc;
		pHeight->GetLevelDesc( 0, &desc );
		if ( FAILED( hr = D3DXCreateTexture( MUTState()->GetDevice(), desc.Width, desc.Height, 0, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &pTex ) ) ){
			MUTDXErrorTrace( hr, L"heightmap normal failed" );
			return E_FAIL;
		}
		Heightmap2Normalmap( pHeight, strength, pTex );
		pHeight->Release();
	}else{
		TString fn( tmp );
		if ( !CheckTGASuffix( tmp ) ){
			fn += TString( L".tga" );
		}
		if ( FAILED( hr = D3DXCreateTextureFromFile( MUTState()->GetDevice(), fn, &pTex ) ) ){
			MUTDXErrorTrace( hr, L"normal map failed" );
			return E_FAIL;
		}
	}
	return S_OK;
}

HRESULT CMUTMD5Stage::CreateStage(){
	HRESULT hr = S_OK;
	if ( blendtype == L"diffusemap" ){
		if ( !CheckTGASuffix( mapname ) )
			mapname += TString( L".tga" );
		if ( FAILED( hr = D3DXCreateTextureFromFile( MUTState()->GetDevice(), mapname, &pTex ) ) ){
			MUTDXErrorTrace( hr, L"stage diffuse texture failed" );
			return E_FAIL;
		}
	}else if ( blendtype == L"bumpmap" ){
		LoadBumpMap( mapname, pTex );
	}else if ( blendtype ==  L"specular" ){
		if ( !CheckTGASuffix( mapname ) )
			mapname += TString( L".tga" );
		if ( FAILED( hr = D3DXCreateTextureFromFile( MUTState()->GetDevice(), mapname, &pTex ) ) ){
			MUTDXErrorTrace( hr, L"stage specular texture failed" );
			return E_FAIL;
		}
	}
	return S_OK;
}

void CMUTMD5Stage::ParseStage( CMUTParser& parser ){
	static char tmp[256];
	while ( true ){
		if ( parser.CheckTokenString( "blend" ) ){
			if ( parser.CheckTokenString( "diffusemap" ) ){
				blendtype = "diffusemap";
			}
			else if ( parser.CheckTokenString( "bumpmap" ) ){
				blendtype = "bumpmap";
			}
			else if ( parser.CheckTokenString( "filter" ) ){
				blendtype = "filter";
			}
			else 
				parser.SkipLine();
		}
		else if ( parser.CheckTokenString( "map" ) ){
			parser.ParseLine( tmp );
			mapname = tmp;
		}
		else if ( parser.CheckTokenString( "alphaTest" ) ){
			bAlphaTest = TRUE;
			if ( parser.IsFloat() )
				alphaTestRef = parser.ParseFloat();
			else
				bAlphaTest = FALSE;
		}
		else if ( parser.CheckTokenString( "}" ) )
			break;
		else if ( parser.IsComment() )
			parser.SkipComment();	
		else
			//skip unknown token
			parser.SkipLine();
	}
}

HRESULT CMUTMD5Shader::CreateShader(){
	if ( bCreated ) return S_OK;
	HRESULT hr = S_OK;
	if ( diffusemap.Length() ){
		if ( !CheckTGASuffix( diffusemap ) )
			diffusemap += TString( L".tga" );
		if ( FAILED( hr = D3DXCreateTextureFromFile( MUTState()->GetDevice(), diffusemap, &pDiffuseTex ) ) ){
			MUTDXErrorTrace( hr, L"shader diffusemap failed" );
			return E_FAIL;
		}
	}
	if ( bumpmap.Length() ){
		LoadBumpMap( bumpmap, pBumpTex );
	}
	if ( specularmap.Length() ){
		if ( !CheckTGASuffix( specularmap ) )
			specularmap += TString( L".tga" );
		if ( FAILED( hr = D3DXCreateTextureFromFile( MUTState()->GetDevice(), specularmap, &pSpecularTex ) ) ){
			MUTDXErrorTrace( hr, L"shader specular map failed!" );
			return E_FAIL;
		}
	}
	for ( int i = 0; i < stages.size(); i++ ){
		if ( FAILED ( stages[i].CreateStage() ) ){
			MUTErrorTrace( L"stage failed" );
			return E_FAIL;
		}
	}
	bCreated = TRUE;
	return S_OK;
}

void CMUTMD5Shader::ParseShader( CMUTParser& parser ){
	SkipUnnecessary( parser );
	parser.ExpectTokenString( "{" );
	static char tmp[256];
	while( true ){
		if ( parser.CheckTokenString( "diffusemap" ) ){
			parser.ParseToken( tmp );
			diffusemap = tmp;
		}
		else if ( parser.CheckTokenString( "bumpmap" ) ){
			parser.ParseLine( tmp );
			bumpmap = tmp;
		}
		else if ( parser.CheckTokenString( "specularmap" ) ){
			parser.ParseToken( tmp );
			specularmap = tmp;
		}
		else if ( parser.CheckTokenString( "{" ) ){
			// parse-stage
			stages.push_back(CMUTMD5Stage());
			CMUTMD5Stage& s = stages[ stages.size() - 1 ];
			s.ParseStage( parser );
		}
		else if ( parser.CheckTokenString( "}" ) ){
			break;
		}
		else if ( parser.IsComment() )
			parser.SkipComment();
		else
			//Skip unknown token
			parser.SkipLine();
	}
}

void CMUTMD5Stage::Prepare( LPD3DXEFFECT pEffect ){
	LPDIRECT3DDEVICE9 pDevice = 0;
	pEffect->GetDevice( &pDevice );

	if ( blendtype == L"diffusemap" ){
		pDevice->SetTexture( 0, pTex );
	}else if ( blendtype == L"bumpmap" ){
		pDevice->SetTexture( 1, pTex );
		pDevice->SetSamplerState( 1, D3DSAMP_MIPFILTER, D3DTEXF_NONE );
	}
}

void CMUTMD5Shader::Prepare( LPD3DXEFFECT pEffect ){
	LPDIRECT3DDEVICE9 pDevice = 0;
	pEffect->GetDevice( &pDevice );

	for ( int i = 0; i < stages.size(); i++ ){
		stages[i].Prepare( pEffect );
	}

	if ( pDiffuseTex )
		pDevice->SetTexture( 0, pDiffuseTex );
	if ( pBumpTex ){
		pDevice->SetTexture( 1, pBumpTex );
		pDevice->SetSamplerState( 1, D3DSAMP_MIPFILTER, D3DTEXF_NONE );
	}
	if ( pSpecularTex )
		pDevice->SetTexture( 2, pSpecularTex );
}

void SkipUnnecessary( CMUTParser& parser ){
	while ( true ){
		if ( parser.IsComment() ){
			parser.SkipComment();
		}else if ( parser.CheckTokenString("table") ){
			parser.SkipTable();
		}else
			break;
	}
}

HRESULT CMUTMD5Material::LoadMaterialFromFile( LPWSTR fn ){
	HANDLE hFile = CreateFile( fn, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
	if ( !hFile ){
		MUTWinErrorTrace( GetLastError() );
		return E_FAIL;
	}

	CMUTParser parser;
	parser.OpenHandle( hFile );
	
	while ( !parser.End() ){
		SkipUnnecessary( parser );
		static char shadername[128];
		shadername[ 0 ] = 0;
		parser.ParseToken( shadername );
		if ( shadername[ 0 ] == 0 )
			break;
		MUTLog( shadername );
		CMUTMD5Shader* shader = new CMUTMD5Shader;
		shader->ParseShader( parser );
		mtr[ TString( shadername ) ] = shader;
	}
	
	parser.Close();
	return S_OK;
}
CMUTMD5Material g_mtr;
//////////////////////////////////////////////////////////////////


void CMUTMD5Anim::GetFrame( int iframe, CMUTMD5Joint* js ){
	if ( iframe > numFrames )
		iframe = iframe % numFrames;
	float* curframes = frames + iframe * numAnimatedComponents;
	for ( int i = 0; i < numJoints; i++ ){
		int j = 0, start = startIndices[ i ];
		js[ i ].q = joints[ i ].q;
		js[ i ].t = joints[ i ].t;
		if ( flags[ i ] & 0x01 ){ // Tx
			js[ i ].t.x = curframes[ start + j++ ];
		}
		if ( flags[ i ] & 0x02 ){ // Ty
			js[ i ].t.y = curframes[ start + j++ ];
		}
		if ( flags[ i ] & 0x04 ){ // Tz
			js[ i ].t.z = curframes[ start + j++ ];
		}
		if ( flags[ i ] & 0x08 ){ // Qx
			js[ i ].q.x = curframes[ start + j++ ];
		}
		if ( flags[ i ] & 0x10 ){ // Qy
			js[ i ].q.y = curframes[ start + j++ ];
		}
		if ( flags[ i ] & 0x20 ){ // Qz
			js[ i ].q.z = curframes[ start + j++ ];
		}
		D3DXQUATERNION& q = js[ i ].q;
		js[ i ].q.w = sqrt( fabs( 1.0f - q.x*q.x - q.y*q.y - q.z*q.z ) );
		if ( joints[ i ].parent ){
			int parentNum = joints[ i ]. parent - joints;
			D3DXMATRIX matQuat;
			D3DXMatrixRotationQuaternion( &matQuat, &js[ parentNum ].q );
			D3DXMatrixTranspose( &matQuat, &matQuat );
			D3DXVec3TransformCoord( &js[ i ].t, &js[ i ].t, &matQuat );
			js[ i ].t += js[ parentNum ].t;
			D3DXQuaternionMultiply( &js[ i ].q, &js[ parentNum ].q, &js[ i ].q );
		}
	}
}

HRESULT CMUTMD5Anim::LoadAnimFromFile( LPWSTR fn ){
	HANDLE hFile = CreateFile( fn, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if ( !hFile ){
		MUTWinErrorTrace( GetLastError() );
		return E_FAIL;
	}
	CMUTParser parser;
	parser.OpenHandle( hFile );

	parser.ExpectTokenString( MD5_VERSION_STRING );
	int version = parser.ParseInt();
	if ( version != MD5_VERSION ){
		MUTErrorTrace( L"MD5Anim version invalid" );
		return E_FAIL;
	}

	parser.ExpectTokenString( "commandline" );
	parser.SkipLine();

	parser.ExpectTokenString( "numFrames" );
	numFrames = parser.ParseInt();

	parser.ExpectTokenString( "numJoints" );
	numJoints = parser.ParseInt();
	joints = new CMUTMD5Joint[ numJoints ];
	flags = new int[ numJoints ];
	startIndices = new int[ numJoints ];

	parser.ExpectTokenString( "frameRate" );
	frameRate = parser.ParseInt();
		
	parser.ExpectTokenString( "numAnimatedComponents" );
	numAnimatedComponents = parser.ParseInt();

	frames = new float[ numAnimatedComponents * numFrames ];

	ParseHierarchy( parser );

	ParseBounds( parser );

	ParseBaseframe( parser );

	for ( int i = 0; i < numFrames; i++ ){
		ParseFrame( parser );
	}

	parser.Close();
	return S_OK;
}

void CMUTMD5Anim::ParseHierarchy( CMUTParser& parser ){
	parser.ExpectTokenString( "hierarchy" );
	parser.ExpectTokenString( "{" );
	
	for ( int i = 0; i < numJoints; i++ ){
		static char jointname[64];
		parser.ParseQuoteString( jointname );
		int parent = parser.ParseInt();
		flags[ i ] = parser.ParseInt();
		startIndices[ i ] = parser.ParseInt();

		if ( parent < 0 )
			joints[ i ].parent = NULL;
		else
			joints[ i ].parent = &joints[ parent ];

		parser.SkipComment();
	}

	parser.ExpectTokenString( "}" );
}

void CMUTMD5Anim::ParseBounds( CMUTParser& parser ){
	parser.ExpectTokenString( "bounds" );
	parser.ExpectTokenString( "{" );

	D3DXVECTOR3 minPos, maxPos;
	for ( int i = 0; i < numFrames; i++ ){
		parser.ParseBracketVec3( minPos );
		parser.ParseBracketVec3( maxPos );
	}

	parser.ExpectTokenString( "}" );
}

void CMUTMD5Anim::ParseBaseframe( CMUTParser& parser ){
	parser.ExpectTokenString( "baseframe" );
	parser.ExpectTokenString( "{" );

	D3DXVECTOR3 q;
	for ( int i = 0; i < numJoints; i++ ){
		parser.ParseBracketVec3( joints[ i ].t );
		parser.ParseBracketVec3( q );
		joints[ i ].q = D3DXQUATERNION( q.x, q.y, q.z, sqrt( fabs( 1.0f - q.x*q.x - q.y*q.y - q.z*q.z ) ) );
	}

	parser.ExpectTokenString( "}" );
}

void CMUTMD5Anim::ParseFrame( CMUTParser& parser ){
	parser.ExpectTokenString( "frame" );
	int frameIndex = parser.ParseInt();
	parser.ExpectTokenString( "{" );

	for ( int i = 0; i < numAnimatedComponents; i++ ){
		frames[ frameIndex*numAnimatedComponents + i ] = parser.ParseFloat();
	}

	parser.ExpectTokenString( "}" );
}

void CMUTMD5Anim::Release(){
	if ( joints )
		delete[] joints;
	if ( flags )
		delete[] flags;
	if ( startIndices )
		delete[] startIndices;
	if ( frames )
		delete[] frames;
	joints = 0;
	flags = 0;
	startIndices = 0;
	frames = 0;
}


CMUTMD5Anim* CMUTMD5AnimManager::GetAnim( TString& name ){
	auto itr = manim.find( name );
	if ( itr != manim.end() )
		return itr->second;
	return NULL;
}

HRESULT CMUTMD5AnimManager::LoadAnimFromFile( LPWSTR fn ){
	if ( manim.find( fn ) != manim.end() )
		return E_FAIL;
	CMUTMD5Anim* pAnim = new CMUTMD5Anim;
	pAnim->LoadAnimFromFile( fn );
	manim[ fn ] = pAnim;
	return S_OK;
}

void CMUTMD5AnimManager::Release(){
	for ( auto itr = manim.begin(); itr != manim.end(); itr++ ){
		CMUTMD5Anim* pAnim = itr->second;
		pAnim->Release();
		delete pAnim;
	}
	manim.clear();
}

///////////////////////////////////////////////////////////////////
D3DVERTEXELEMENT9 g_velem[] = {
/*	stream, offset, Type,				Method, Usage,					UsageIndex */
	{0,		0,		D3DDECLTYPE_FLOAT4, 0,		D3DDECLUSAGE_POSITION,	0 },
	{0,		16,		D3DDECLTYPE_FLOAT3, 0,		D3DDECLUSAGE_NORMAL,	0 },
	{0,		28,		D3DDECLTYPE_FLOAT3, 0,		D3DDECLUSAGE_TANGENT,	0 },
	{0,		40,		D3DDECLTYPE_FLOAT2, 0,		D3DDECLUSAGE_TEXCOORD,	0 },
	D3DDECL_END()
};
LPDIRECT3DVERTEXDECLARATION9 g_vdecl = NULL;
int g_vdeclCount = 0;




HRESULT CMUTMD5Mesh::CreateVB(){
	LPDIRECT3DDEVICE9 pDevice = MUTState()->GetDevice();
	if (!pDevice) return E_FAIL;
	HRESULT hr;
	if ( FAILED( hr = pDevice->CreateVertexBuffer( vnum * sizeof( MD5VERTEX ) , 0, 0, D3DPOOL_MANAGED, &vb, NULL ) ) ){
		MUTDXErrorTrace( hr, L"Create MD5Mesh VertexBuffer failed" );
		return E_FAIL;
	}
	if ( !g_vdecl && FAILED( hr = pDevice->CreateVertexDeclaration( g_velem, &g_vdecl ) ) ){
		MUTDXErrorTrace( hr, L"VertexDecl failed" );
		return E_FAIL;
	}
	g_vdeclCount ++;
	return S_OK;
}

HRESULT CMUTMD5Mesh::CreateIB(){
	LPDIRECT3DDEVICE9 pDevice = MUTState()->GetDevice();
	if (!pDevice) return E_FAIL;
	HRESULT hr;
	if ( FAILED( hr = pDevice->CreateIndexBuffer( 3 * fnum * sizeof(WORD), 0, D3DFMT_INDEX16, D3DPOOL_MANAGED, &ib, NULL) ) ){
		MUTDXErrorTrace( hr, L"Create MD5Mesh IndexBuffer failed");
		return E_FAIL;
	}
	return S_OK;
}

void CMUTMD5Mesh::Release(){
	if (firstWeight)
		delete[] firstWeight;
	if (numWeight)
		delete[] numWeight;
	if (weightIndex)
		delete[] weightIndex;
	if (scaledWeight)
		delete[] scaledWeight;
	if (vb) 
		vb->Release();
	if (ib)
		ib->Release();
	vb = 0, ib = 0;
	firstWeight = numWeight = weightIndex = 0;
	scaledWeight = 0;

	g_vdeclCount --;
	if ( !g_vdeclCount ){
		g_vdecl->Release();
		g_vdecl = 0;
	}
}

void CMUTMD5Mesh::ParseMesh( CMUTParser& parser ){
	parser.ExpectTokenString( "mesh" );
	parser.ExpectTokenString( "{" );
	parser.SkipComment();

	if ( parser.CheckTokenString( "name" ) ){
		static char nametmp[64];
		parser.ParseToken(nametmp);
	}

	parser.ExpectTokenString( "shader" );
	static char shadertmp[1024];
	parser.ParseQuoteString( shadertmp );
	pShader = g_mtr.GetShader( TString( shadertmp ) );
	if ( pShader )
		pShader->CreateShader();

	parser.ExpectTokenString( "numverts" );
	vnum = parser.ParseInt();

	firstWeight = new int[vnum];
	if ( !firstWeight ){
		MUTErrorTrace(L"firstWeight null");
		return;
	}
	numWeight = new int[vnum];
	if ( !numWeight ){
		MUTErrorTrace(L"numWeight null");
		return;
	}

	HRESULT hr = S_OK;
	if ( FAILED( hr = CreateVB() ) ){
		MUTDXErrorTrace( hr, L"VertexBuffer failed" );
		return;
	}

	int index = 0;
	MD5VERTEX* pVertex = NULL;
	vb->Lock( 0, 0, (void**)&pVertex, 0 );
	for ( int i = 0; i < vnum; i++, pVertex++ ){
		parser.ExpectTokenString( "vert" );
		index = parser.ParseInt();
		parser.ParseBracketVec2( pVertex->uv );
		//pVertex->uv.y = 1.0f - pVertex->uv.y;
		firstWeight[i] = parser.ParseInt();
		numWeight[i] = parser.ParseInt();
	}
	vb->Unlock();

	parser.ExpectTokenString( "numtris" );
	fnum = parser.ParseInt();
	if ( FAILED( hr = CreateIB() ) ){
		MUTDXErrorTrace( hr, L"IndexBuffer failed" );
		return;
	}
	WORD* pIndex = NULL;
	ib->Lock( 0, 0, (void**)&pIndex, 0 );
	for ( int i = 0; i < fnum; i++ ){
		parser.ExpectTokenString( "tri" );
		index = parser.ParseInt();
		pIndex[ i*3 + 0 ] = parser.ParseInt();
		pIndex[ i*3 + 1 ] = parser.ParseInt();
		pIndex[ i*3 + 2 ] = parser.ParseInt();
	}
	ib->Unlock();

	parser.ExpectTokenString( "numweights" );
	iNumWeight = parser.ParseInt();

	normals = new D3DXVECTOR3[ iNumWeight ];
	tangents = new D3DXVECTOR3[ iNumWeight ];
	ZeroMemory( normals, sizeof( normals ) );
	ZeroMemory( tangents, sizeof( tangents ) );

	weightIndex = new int[ iNumWeight ];
	if ( !weightIndex ){
		MUTErrorTrace( L"weightIndex failed" );
		return;
	}
	ZeroMemory( weightIndex, sizeof( weightIndex ) );

	scaledWeight = new D3DXVECTOR4[ iNumWeight ];
	if ( !scaledWeight ){
		MUTErrorTrace( L"scaledWeight failed" );
		return;	
	}
	for ( int i = 0; i < iNumWeight; i++ ){
		parser.ExpectTokenString( "weight" );
		index = parser.ParseInt();
		weightIndex[ i ] = parser.ParseInt();
		float jointweight = parser.ParseFloat();
		parser.ParseBracketVec3( *( D3DXVECTOR3 * )&scaledWeight[i] );
		scaledWeight[ i ].w = 1.0f;
		scaledWeight[ i ] *= jointweight;
	}

	parser.ExpectTokenString("}");
}

HRESULT CMUTMD5Model::Create( LPWSTR fn ){
	HANDLE hFile = CreateFile(fn, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (!hFile)
		MUTWinErrorTrace( GetLastError() );
	CMUTParser parser;
	parser.OpenHandle( hFile );
	
	parser.ExpectTokenString( MD5_VERSION_STRING );
	version = parser.ParseInt();
	if ( version != MD5_VERSION ){
		MUTErrorTrace(L"Invalid md5 version");
		return E_FAIL;
	}

	parser.ExpectTokenString( "commandline" );
	parser.SkipLine();

	parser.ExpectTokenString( "numJoints" );
	numJoints = parser.ParseInt();
	joints = new CMUTMD5Joint[ numJoints ];	
	jointsA = new CMUTMD5Joint[ numJoints ];
	jointsB = new CMUTMD5Joint[ numJoints ];
	jointmats = new D3DXMATRIX[ numJoints ];

	parser.ExpectTokenString( "numMeshes" );
	numMeshes = parser.ParseInt();
	meshes = new CMUTMD5Mesh[ numMeshes ];

	parser.ExpectTokenString( "joints" );
	parser.ExpectTokenString( "{" );
	/*	for each
		"joint_name" parent_index defaultPose(trans_vec3, quat_vec3) // comment
		*/
	for ( int i = 0; i < numJoints; i++ ){
		ParseJoints( parser, joints+i, joints );
	}
	parser.ExpectTokenString( "}" );

	/*  for each 
		mesn {	// comment
			shader "path"
			numverts int
			vert index ( texcoord_vec2 ) firstWeight numWeight
			...
			numtris int
			tri index t0_int t1_int t2_int
			...
			numweights int
			weight index jointnum jointweight_float ( offset_vec3 )
		} */
	for ( int i = 0; i < numMeshes; i++ ){
		meshes[i].ParseMesh( parser );
	}

	parser.Close();

	TransformVerts();
	for ( int i = 0; i < numJoints; i++ ){
		D3DXMatrixInverse( &jointmats[i], NULL, &jointmats[i] );
	}
	for ( int i = 0; i < numMeshes; i++ ){
		meshes[i].ComputeNormal( jointmats );
		meshes[i].ComputeTangent( jointmats );
	}
	return S_OK;
}

void CMUTMD5Model::ParseJoints( CMUTParser& parser, CMUTMD5Joint* curjoint, CMUTMD5Joint* joints ){
	parser.ParseQuoteString( curjoint->name );
	int parent = parser.ParseInt();
	if ( parent < 0 )
		curjoint->parent = NULL;
	else
		curjoint->parent = &joints[ parent ];
	parser.ParseBracketVec3( curjoint->t );
	D3DXVECTOR3 q;
	parser.ParseBracketVec3( q );
	curjoint->q = D3DXQUATERNION( q.x, q.y, q.z, sqrt( fabs( 1.0f - q.x*q.x - q.y*q.y - q.z*q.z ) ) );
	parser.SkipComment();
}

CMUTMD5AnimManager g_animManager;

void CMUTMD5Model::SetAnim( TString& name ){
	pAnim = g_animManager.GetAnim( name );
}

void CMUTMD5Model::UpdateFrame( float frame ){
	if ( pAnim ){
		frame = frame * pAnim->frameRate;
		pAnim->GetFrame( (int)frame, jointsA );
		pAnim->GetFrame( (int)frame + 1, jointsB );
		float fLerp = frame - floor( frame );
		for ( int i = 0; i < numJoints; i++ ){
			D3DXVec3Lerp( &joints[ i ].t, &jointsA[ i ].t, &jointsB[ i ].t, fLerp );
			D3DXQuaternionSlerp( &joints[ i ].q, &jointsA[ i ].q, &jointsB[ i ].q, fLerp);
		}
	}
	TransformVerts();
}
void CMUTMD5Model::Render( LPD3DXEFFECT pEffect ){
	LPDIRECT3DDEVICE9 pDevice = MUTState()->GetDevice();
	if ( !pDevice ) return;
	for ( int i = 0; i < numMeshes; i++ ){
		meshes[ i ].Render( pDevice, pEffect );
	}
}

void CMUTMD5Model::ComputeNormal(){/*
	for ( int i = 0; i < numMeshes; i++ ){
		meshes[ i ].ComputeNormal();
	}*/
}
void CMUTMD5Model::ComputeTangent(){/*
	for ( int i = 0; i < numMeshes; i++ ){
		meshes[ i ].ComputeTangent();
	}*/
}

void CMUTMD5Mesh::ComputeNormal(  D3DXMATRIX* invmats ){
	MD5VERTEX* verts = 0;
	vb->Lock( 0, 0, (void**)&verts, 0 );
	WORD* tris = 0;
	ib->Lock( 0, 0, (void**)&tris, 0 );
	MD5VERTEX *v0 = 0, *v1 = 0, *v2 = 0;

	for ( int v = 0; v < vnum; v++ )
		verts[ v ].normal = D3DXVECTOR3( 0, 0, 0 );

	D3DXVECTOR3 normal;
	for ( int f = 0; f < fnum; f++ ){
		v0 = verts + tris[ 3*f + 0 ];
		v1 = verts + tris[ 3*f + 1 ];
		v2 = verts + tris[ 3*f + 2 ];
		D3DXVec3Cross( &normal, &D3DXVECTOR3(v1->pos-v0->pos), &D3DXVECTOR3(v2->pos-v0->pos) );
		D3DXVec3Normalize( &normal, &normal );
		v0->normal += normal;
		v1->normal += normal;
		v2->normal += normal;
	}

	for ( int v = 0; v < vnum; v++ ){
		D3DXVec3Normalize( &verts[v].normal, &verts[v].normal );
	}

	for ( int j = 0; j < iNumWeight; j++ ){
		normals[j].x = normals[j].y = normals[j].z = 0;
	}

	for ( int v = 0; v < vnum; v++ ){
		for ( int j = 0; j < numWeight[ v ]; j++ ){
			int index = firstWeight[ v ] + j;
			D3DXVECTOR3 nout;
			D3DXVec3TransformNormal( &nout, &verts[v].normal, invmats+weightIndex[ index ] );
			normals[ index ] += nout;
		}
	}
	
	for ( int j = 0; j < iNumWeight; j++ ){
		D3DXVec3Normalize( normals+j, normals+j );
	}
		
	ib->Unlock();
	vb->Unlock();
}

void CMUTMD5Mesh::ComputeTangent( D3DXMATRIX* invmats ){
	//D3DXVECTOR3* tan1 = new D3DXVECTOR3[ vnum*2 ];
	//D3DXVECTOR3* tan2 = tan1 + vnum;
	//ZeroMemory( tan1, sizeof(D3DXVECTOR3)*vnum*2 );

	MD5VERTEX* verts = 0;
	vb->Lock( 0, 0, (void**)&verts, 0 );
	WORD* tris = 0;
	ib->Lock( 0, 0, (void**)&tris, 0 );

	for ( int v = 0; v < vnum; v++ ){
		verts[v].tangent = D3DXVECTOR3( 0, 0, 0 );
	}

	for ( int f = 0; f < fnum; f++ ){
		WORD i1 = tris[ 3*f + 0 ];
		WORD i2 = tris[ 3*f + 1 ];
		WORD i3 = tris[ 3*f + 2 ];
		D3DXVECTOR4& v1 = verts[ i1 ].pos;
		D3DXVECTOR4& v2 = verts[ i2 ].pos;
		D3DXVECTOR4& v3 = verts[ i3 ].pos;
		D3DXVECTOR2& w1 = verts[ i1 ].uv;
		D3DXVECTOR2& w2 = verts[ i2 ].uv;
		D3DXVECTOR2& w3 = verts[ i3 ].uv;

		float x1 = v2.x - v1.x;
		float x2 = v3.x - v1.x;
		float y1 = v2.y - v1.y;
		float y2 = v3.y - v1.y;
		float z1 = v2.z - v1.z;
		float z2 = v3.z - v1.z;

		float s1 = w2.x - w1.x;
		float s2 = w3.x - w1.x;
		float t1 = w2.y - w1.y;
		float t2 = w3.y - w1.y;
	
		float r = 1.0f; // ( s1*t2 - t1*s2 );
		D3DXVECTOR3 sdir( (t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r );

		D3DXVec3Normalize( &sdir, &sdir );

		verts[ i1 ].tangent += sdir;
		verts[ i2 ].tangent += sdir;
		verts[ i3 ].tangent += sdir;
	}

	for ( int v = 0; v < vnum; v++ ){
		D3DXVec3Normalize( &verts[v].tangent, &verts[v].tangent );
	}

	for ( int j = 0; j < iNumWeight; j++ )
		tangents[j].x = tangents[j].y = tangents[j].z = 0;

	for ( int v = 0; v < vnum; v++ ){
		for ( int j = 0; j < numWeight[v]; j++ ){
			int index = firstWeight[v]+j;
			D3DXVECTOR3 tout;
			D3DXVec3TransformNormal( &tout, &verts[v].tangent, invmats+weightIndex[ index ] );
			tangents[ index ] += tout;
		}
	}

	for ( int j = 0; j < iNumWeight; j++ ){
		D3DXVec3Normalize( tangents+j, tangents+j );
	}

	ib->Unlock();
	vb->Unlock();
	//delete[] tan1;
}

void CMUTMD5Model::TransformVerts(){
	for ( int i = 0; i < numJoints; i++ ){
		D3DXMatrixRotationQuaternion( &jointmats[ i ], &joints[ i ].q );
		D3DXMatrixTranspose( &jointmats[i], &jointmats[i] );
		jointmats[ i ]._41 = joints[ i ].t.x;
		jointmats[ i ]._42 = joints[ i ].t.y;
		jointmats[ i ]._43 = joints[ i ].t.z;
		if ( joints[ i ].parent ){
			// already transformed by its parent
		}
	}
	for ( int i = 0; i < numMeshes; i++ ){
		meshes[ i ].TransformVerts( jointmats );
	}
}

void CMUTMD5Mesh::TransformVerts( D3DXMATRIX* jointmats ){
	MD5VERTEX* pVertex = NULL;
	D3DXVECTOR4 v4( 0, 0, 0, 0 );
	D3DXVECTOR3 nout( 0, 0, 0 ), tout( 0, 0, 0 );
	vb->Lock( 0, 0, (void**)&pVertex, 0 );
	for ( int i = 0; i < vnum; i++ ){
		pVertex[ i ].pos = D3DXVECTOR4( 0, 0, 0, 0 );
		pVertex[ i ].normal = D3DXVECTOR3( 0, 0, 0 );
		for ( int j = 0; j < numWeight[ i ]; j++ ){
			int index = firstWeight[ i ] + j;
			D3DXVec4Transform( &v4, &scaledWeight[ index ], &jointmats[ weightIndex[ index ] ] );
			D3DXVec3TransformNormal( &nout, &normals[ index ], &jointmats[ weightIndex[ index ] ] );
			D3DXVec3TransformNormal( &tout, &tangents[ index ], &jointmats[ weightIndex[ index ] ] );
			pVertex[ i ].normal += nout*scaledWeight[ index ].w;
			pVertex[ i ].tangent += tout*scaledWeight[ index ].w;
			pVertex[ i ].pos += v4;
		}
		pVertex[ i ].pos.w = 1.0f;
		swap( pVertex[i].pos.y, pVertex[i].pos.z );
		D3DXVec3Normalize( &pVertex[ i ].normal, &pVertex[ i ].normal );
		D3DXVec3Normalize( &pVertex[ i ].tangent, &pVertex[ i ].tangent );
	}
	vb->Unlock();
}

void CMUTMD5Mesh::Render( LPDIRECT3DDEVICE9 pDevice, LPD3DXEFFECT pEffect ){
	if ( pShader ){
		pShader->Prepare( pEffect );
		//pDevice->SetSamplerState( 1, D3DSAMP_MIPFILTER, D3DTEXF_NONE );
		pDevice->SetVertexDeclaration( g_vdecl );
		pDevice->SetStreamSource( 0, vb, 0, sizeof( MD5VERTEX ) );
		pDevice->SetIndices( ib );
		pDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 0, 0, vnum, 0, fnum );
	}
	/*else
		pDevice->SetTexture( 0, 0 );*/
}

typedef CMUTSimpleManager<CMUTMD5Model> CMUTMD5ModelManager;
CMUTMD5ModelManager g_md5mgr;

BOOL CMUTCharacter::BindModel( LPWSTR dir ){
	manim.clear();
	WIN32_FIND_DATA findData;
	WCHAR tmp[256];
	wsprintf( tmp, L"%s/*.*", dir );
	HANDLE hFind = FindFirstFile( tmp, &findData );
	if ( hFind == INVALID_HANDLE_VALUE ){
		return E_FAIL;
	}
	do{
		if ( wcscmp( findData.cFileName, L"." ) == 0 || wcscmp( findData.cFileName, L".." ) == 0 )
			continue;
		wsprintf( tmp, L"%s/%s", dir, findData.cFileName );
		if ( wcsstr( findData.cFileName, L".md5mesh" ) ){
			pModel = g_md5mgr.Get( tmp );
		} else if ( wcsstr( findData.cFileName, L".md5anim" ) ){
			g_animManager.LoadAnimFromFile( tmp );
			WCHAR* wdot = wcschr( findData.cFileName, L'.' );
			*wdot = 0;
			manim[ findData.cFileName ] = tmp;
		}
	} while ( FindNextFile( hFind, &findData ) );
	return FindClose( hFind );
}

void CMUTCharacter::SetAnim( LPWSTR name ){
	auto itr = manim.find( name );
	if ( itr != manim.end() )
		pModel->SetAnim( itr->second );
}

void CMUTCharacter::SetAnim( int cnt ){
	cnt %= manim.size();
	auto itr = manim.begin();
	while ( cnt && itr != manim.end() ){
		itr++;
		cnt--;
	}
	pModel->SetAnim( itr->second );
}

void CMUTCharacter::UpdateFrame(){
	frame += MUTState()->GetElapsedTime();
}

void CMUTCharacter::Render( LPD3DXEFFECT pEffect ){
	D3DXVECTOR3 vLeft;
	D3DXVec3Cross( &vLeft, &dir, &D3DXVECTOR3( 0, 1, 0 ) );
	D3DXVec3Normalize( &vLeft, &vLeft );
	D3DXVec3Normalize( &dir, &dir );
	D3DXMATRIX matWorld;
	matWorld._11 = dir.x; matWorld._12 = 0; matWorld._13 = vLeft.x; matWorld._14 = 0;
	matWorld._21 = dir.y; matWorld._22 = 1; matWorld._23 = vLeft.y; matWorld._24 = 0;
	matWorld._31 = dir.z; matWorld._32 = 0; matWorld._33 = vLeft.z; matWorld._34 = 0;
	matWorld._41 = pos.x; matWorld._42 = pos.y; matWorld._43 = pos.z; matWorld._44 = 1;
	pEffect->SetMatrix( "g_matWorld", &matWorld );
	pEffect->CommitChanges();
	pModel->UpdateFrame( frame );
	pModel->Render( pEffect );
}

CMUTCharacter g_md5;
void MUTModelTest( LPDIRECT3DDEVICE9 pDevice ){
	g_mtr.LoadMaterialFromFile( L"materials/characters.mtr" );
	g_mtr.LoadMaterialFromFile( L"materials/char_common.mtr" );
	g_mtr.LoadMaterialFromFile( L"materials/weapons.mtr" );
	g_mtr.LoadMaterialFromFile( L"materials/monsters.mtr" );
	g_mtr.LoadMaterialFromFile( L"materials/senetemp.mtr" );
	g_mtr.LoadMaterialFromFile( L"materials/kentest.mtr" );
	g_md5.BindModel( L"models/md5/monsters/cyberdemon" );
}

void MUTModelRenderTest(){/*
	D3DXMATRIX matOldView, matView, matWorld;
	LPDIRECT3DDEVICE9 pDevice = MUTState()->GetDevice();
	if ( ! pDevice ) return;
	pDevice->GetTransform( D3DTS_VIEW, &matOldView );
	matView = matOldView;
	matView._41  = matView._42 = 0;
	matView._43 = 400;
	D3DXMatrixRotationX( &matWorld, -D3DX_PI/2.0f );
	pDevice->SetTransform( D3DTS_WORLD, &matWorld );
	pDevice->SetTransform( D3DTS_VIEW, &matView );
	g_md5.Render();
	pDevice->SetTransform( D3DTS_VIEW, &matView );*/
}